 package com.gdteam.kernel.httpserver.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.tapestry5.ioc.annotations.EagerLoad;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.InjectResource;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.restlet.Component;
import org.restlet.data.Protocol;
import org.slf4j.Logger;

import com.gdteam.kernel.conf.Environment;
import com.gdteam.kernel.httpserver.ApplicationNotSupportedException;
import com.gdteam.kernel.httpserver.HttpApplication;
import com.gdteam.kernel.httpserver.HttpServer;
import com.gdteam.kernel.main.ServiceManager;

@EagerLoad
public class RestletHttpServer implements HttpServer {
    
    @InjectResource
    private Logger logger;
    
    private Component component;
    private ServiceManager serviceManager;
    
    @Inject @Symbol(Environment.HTTP_SERVER_PORT)
    private Integer port;
    
    private Integer runningPort = -1;
    
    private Map<String, RestletApplication> applicationMap;
    
    @Inject
    public RestletHttpServer(ServiceManager mgr) {
        this.serviceManager = mgr;
        this.serviceManager.registerOperations(this);
        this.component = new Component();
        this.applicationMap = new HashMap<String, RestletApplication>();
    }
    
    public List<String> getAvailableUris() {
        
        List<String> ret = new ArrayList<String>();
        
        Collection<RestletApplication> applications = this.applicationMap.values();
        
        for (HttpApplication httpApplication : applications) {
            if (httpApplication.isStarted()) {
                String uriPattern = httpApplication.getURIPattern();
                
                for (String resourceUri : httpApplication.getAvailableUris()) {
                    ret.add(uriPattern + resourceUri);
                }
            }
        }
        
        return ret;
    }

    public int getHostPort() {
        return this.runningPort;
    }

    public String getName() {
        return HttpServer.FQN;
    }

    public void start() {
        
        if (this.isStopped()) {
            
            if (port == -1) {
                this.runningPort = -1;
                logger.error("Unable to start the server. No port available");
                
            } else {
                
                //port != -1 => running port assignale
                this.runningPort = this.port;
                this.component.getServers().add(Protocol.HTTP, this.runningPort);
                
                try {
                    this.component.start();
                    logger.info("HTTP Server started on {}", port);
                } catch (Exception e) {
                    this.runningPort = -1;
                    logger.error("Unable to start the server", e);
                }
            }
        } else {
            logger.warn("Http Server already started");
        }
        
    }

    public void stop() {
        if (this.isStopped()) {
            logger.warn("Unable to stop server. It does not exist or was not started");
        } else {
            try {
                this.component.stop();
                this.runningPort = -1;
            } catch (Exception e) {
                logger.error("Unable to stop server", e);
            }
        }
    }
    
    public boolean isStarted() {
        return this.component.isStarted();
    }

    public boolean isStopped() {
        return this.component.isStopped();
    }


    public void deployApplication(HttpApplication application) throws ApplicationNotSupportedException {
        if (!(application instanceof RestletApplication)) {
            throw new ApplicationNotSupportedException(application, RestletApplication.class);
        }
        
        if (!this.applicationMap.containsKey(application)) {
            this.component.getDefaultHost().attach(application.getURIPattern(), (RestletApplication) application);
            this.applicationMap.put(application.getApplicationName(), (RestletApplication) application);
            
            //Add service manager in application context
            ((RestletApplication) application).getContext().getAttributes().put(HttpApplication.SERVICE_MANAGER_KEY, this.serviceManager);
            
        } else {
            logger.warn("Unable to deploy application. {} is already deployed", application.getApplicationName());
        }
        
        
    }

    public void unDeployApplication(String applicationName) {
        RestletApplication application = this.applicationMap.get(applicationName);
        
        if (application != null) {
            this.component.getDefaultHost().detach(application);
            this.applicationMap.remove(applicationName);
        }
    }


    public void startApplication(String applicationName) {
        RestletApplication application = this.applicationMap.get(applicationName);
        
        if (application != null && !application.isStarted()) {
            try {
                application.start();
                //Re-Attach application
                this.component.getDefaultHost().attach(application.getURIPattern(), (RestletApplication) application);
            } catch (Exception e) {
                logger.error("Unable to start application {}", applicationName, e);
            }
        } else {
            logger.warn("Application {} does not exist or is already started", applicationName);
        }
    }


    public void stopApplication(String applicationName) {
        RestletApplication application = this.applicationMap.get(applicationName);
        
        if (application != null && !application.isStopped()) {
            try {
                application.stop();
                //Detach application
                this.component.getDefaultHost().detach(application);
            } catch (Exception e) {
                logger.error("Unable to stop application {}", applicationName, e);
            }
        } else {
            logger.warn("Application {} does not exist or is already stopped", applicationName);
        }
    }
    
}
